package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constans.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.model.list.SearchAttr;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.ManageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: 孙润达
 * @Date: 2022/1/2 12:14
 */
@Service
public class ManageServiceImpl implements ManageService{
    @Autowired
    private BaseCategory1Mapper baseCategory1Mapper;
    @Autowired
    private BaseCategory2Mapper baseCategory2Mapper;
    @Autowired
    private BaseCategory3Mapper baseCategory3Mapper;
    @Autowired
    private BaseAttrValueMapper baseAttrValueMapper;
    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;
    @Autowired
    private  BaseTrademarkMapper baseTrademarkMapper;
    @Autowired
    private BaseSaleAttrMapper baseSaleAttrMapper;
    @Autowired
    private  SpuInfoMapper spuInfoMapper;
    @Autowired
    private  SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private  SpuSaleAttrValueMapper spuSaleAttrValueMapper;
    @Autowired
    private SpuImageMapper spuImageMapper;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private SkuImageMapper skuImageMapper;
    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitService rabbitService;
    //获取一级标题getCategory1
    @Override
    public List<BaseCategory1> getCategory1() {
        return baseCategory1Mapper.selectList(null);
    }

    //获取二级标题getCategory2
    @Override
    public List<BaseCategory2> getCategory2(Long category1Id) {
        return baseCategory2Mapper.selectList(new QueryWrapper<BaseCategory2>()
                .eq("category1_id",category1Id));
    }

    //获取三级标题getCategory3
    @Override
    public List<BaseCategory3> getCategory3(Long category2Id) {
        return baseCategory3Mapper.selectList(new QueryWrapper<BaseCategory3>()
        .eq("category2_id",category2Id));
    }

    //根据多级标题获取详细内容
    @Override
    public List<BaseAttrInfo> attrInfoList(
            Long category1Id,
            Long category2Id,
            Long category3Id) {
       return baseAttrInfoMapper.attrInfoList(category1Id,category2Id,category3Id);

    }
    //添加平台属性
    //分解添加平台属性和属性值，两张表分别添加
    @Override
    public void saveAttrInfo(BaseAttrInfo baseAttrInfo) {
     //更新和保存新的内容（根据  baseAttrInfo.getId()来判断，更新时baseAttrInfo.getId()有值）
        if(baseAttrInfo.getId()!=null){
            //更新
            baseAttrInfoMapper.updateById(baseAttrInfo);
        }else {
            //保存属性新增
            int bavId = baseAttrInfoMapper.insert(baseAttrInfo);
        }
        //修改baseAttrInfo.getId()不为空时，数据会重复添加，可以先把之前的数据先删除之后再添加
        baseAttrValueMapper.delete(new QueryWrapper<BaseAttrValue>()
                .eq("attr_id",baseAttrInfo.getId()));
       //保存属性值
        List<BaseAttrValue> attrValueList = baseAttrInfo.getAttrValueList();
        //如果集合不为空才进行遍历（CollectionUtils是spring公司开发，用于验证集合是否为空）
        if(!CollectionUtils.isEmpty(attrValueList)){
            attrValueList.forEach(attrValue->{
                //设置外键
                attrValue.setAttrId(baseAttrInfo.getId());
                baseAttrValueMapper.insert(attrValue);
            });
        }
    }
    //修改平台属性（回显数据）
    @Override
    public List<BaseAttrValue> getAttrValueList(Long attrId) {
        List<BaseAttrValue> baseAttrValueList = baseAttrValueMapper.selectList(
                new QueryWrapper<BaseAttrValue>().eq("attr_id",attrId));

        return baseAttrValueList;
    }
    //分页查询
    @Override
    public IPage baseTrademark(Integer page, Integer limit) {

        return baseTrademarkMapper.selectPage(new Page<BaseTrademark>(page,limit),null);
    }
    //添加品牌
    @Override
    public void baseTrademarkSave(BaseTrademark baseTrademark) {
        baseTrademarkMapper.insert(baseTrademark);
    }
    //删除品牌
    @Override
    public void remove(Long baseTrademarkId) {
        baseTrademarkMapper.deleteById(baseTrademarkId);
    }
    //查询单个品牌（改回品牌用于回显数据）
    @Override
    public BaseTrademark getByIdBaseTrademark(Long baseTrademarkId) {
       return baseTrademarkMapper.selectById(baseTrademarkId);
    }
    //保存单个品牌（修改品牌）
    @Override
    public void update(BaseTrademark baseTrademark) {
        /*
        * 1.修改品牌信息
        *  直接进行更新操作*/
        baseTrademarkMapper.updateById(baseTrademark);
    }
    //spu分页查询
    @Override
    public IPage spuInfo(Integer page, Integer limit, Long category3Id) {
     return  spuInfoMapper.selectPage(new Page<SpuInfo>(page,limit),
        new QueryWrapper<SpuInfo>().eq("category3_id",category3Id));

    }
    //获取品牌属性值
    @Override
    public List<BaseTrademark> getTrademarkList() {
        List<BaseTrademark> baseTrademarks = baseTrademarkMapper.selectList(null);
        return baseTrademarks;
    }
    //获取销售属性值
    @Override
    public List<BaseSaleAttr> baseSaleAttrList() {
      List<BaseSaleAttr> spuSaleAttrs=  baseSaleAttrMapper.selectList(null);
        return spuSaleAttrs;
    }
    //保存spu内容
    @Override
    public void saveSpuInfo(SpuInfo spuInfo) {
        //spuInfo内分为三部分进行保存s
        spuInfoMapper.insert(spuInfo);
        //1.对图片进行保存
         //1.1先先进行判断是否为空
        List<SpuImage> spuImageList = spuInfo.getSpuImageList();
        if (!CollectionUtils.isEmpty(spuImageList)){
            spuImageList .forEach(spuImage -> {
                spuImage.setSpuId(spuInfo.getId());
                spuImageMapper.insert(spuImage);
            });
        }
        //2.保存销售属性spu_sale_attr
        List<SpuSaleAttr> spuSaleAttrList = spuInfo.getSpuSaleAttrList();
        if (!CollectionUtils.isEmpty(spuSaleAttrList)) {
            spuSaleAttrList.forEach(spuSaleAttr -> {
                spuSaleAttr.setSpuId(spuInfo.getId());
                spuSaleAttrMapper.insert(spuSaleAttr);
                //3.保存销售属性值spu_sale_attr_value
                List<SpuSaleAttrValue> spuSaleAttrValueList = spuSaleAttr.getSpuSaleAttrValueList();
                if (!CollectionUtils.isEmpty(spuSaleAttrValueList)) {
                    spuSaleAttrValueList.forEach(spuSaleAttrValue -> {
                        spuSaleAttrValue.setSpuId(spuSaleAttr.getId());
                        spuSaleAttrValue.setSaleAttrName(spuSaleAttr.getSaleAttrName());
                        spuSaleAttrValueMapper.insert(spuSaleAttrValue);
                    });
                }
            });
        }

    }
    //根据spuId获取图片列表
    @Override
    public List<SpuImage> spuImageList(Long spuId) {
        List<SpuImage> spuImageList =
                spuImageMapper.selectList(new QueryWrapper<SpuImage>()
                .eq("spu_id", spuId));
        return spuImageList;
    }
    //根据spuId获取销售属性
    @Override
    public List<SpuSaleAttr> spuSaleAttrList(Long spuId) {
        List<SpuSaleAttr> spuSaleAttrValueList=
                spuSaleAttrMapper.spuSaleAttrList(spuId);
        return spuSaleAttrValueList;
    }
    //添加sku
    @Override
    public void saveSkuInfo(SkuInfo skuInfo) {
        /*添加sku skuInfo保存数据分为四个模块
            1.sku_info
            2.sku_image
            3.sku_attr_value
            4.sku_sale_attr_value
        * */
        //1.sku_info
        skuInfoMapper.insert(skuInfo);
        //  2.sku_image
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!CollectionUtils.isEmpty(skuImageList)){
            skuImageList .forEach(skuImage -> {
                skuImage.setSkuId(skuInfo.getId());
                skuImageMapper.insert(skuImage);
            });
        }
        //3.sku_attr_value
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if(!CollectionUtils.isEmpty(skuAttrValueList)){
            skuAttrValueList.forEach(skuAttrValue -> {
                skuAttrValue.setSkuId(skuInfo.getId());
                skuAttrValueMapper.insert(skuAttrValue);
            });
        }
        // 4.sku_sale_attr_value
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if (!CollectionUtils.isEmpty(skuSaleAttrValueList)){
            skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
                skuSaleAttrValue.setSkuId(skuInfo.getId());
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
                skuSaleAttrValueMapper.insert(skuSaleAttrValue);
            });
        }
    }
    //分页查询sku
    @Override
    public IPage<SkuInfo> list(Integer page, Integer limit) {
        IPage<SkuInfo> skuInfoIPage = skuInfoMapper.selectPage(new Page<SkuInfo>(page, limit), null);
        return skuInfoIPage;
    }
    //sku上架
    @Override
    public void onSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setIsSale(SkuInfo.IS_SALE);
        skuInfo.setId(skuId);
        skuInfoMapper.updateById(skuInfo);
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS
                ,MqConst.ROUTING_GOODS_UPPER,skuId);

    }
    //sku下架
    @Override
    public void cancelSale(Long skuId) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setIsSale(SkuInfo.NO_SALE);
        skuInfo.setId(skuId);
        skuInfoMapper.updateById(skuInfo);
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS
                ,MqConst.ROUTING_GOODS_LOWER,skuId);
    }



    //通过三级分类id查询分类信息
    @Override
    @GmallCache(prefix = "getCategoryView:")
    public BaseCategoryView getCategoryView(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }
    //获取sku最新价格
    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        QueryWrapper<SkuInfo> wrapper = new QueryWrapper<SkuInfo>();
        //select price from sku_info whrer sku_id
        QueryWrapper<SkuInfo> eq = wrapper.select("price").eq("id", skuId);
        SkuInfo skuInfo = skuInfoMapper.selectOne(eq);
        return skuInfo.getPrice();
        //TODO要时时查看价格将来要。。。。。。
    }
    //根据skuId获取sku信息,根据skuId 查询图片列表集合
/*    @Override
    @GmallCache(prefix = "getSkuInfo:")
    public SkuInfo getSkuInfo(Long skuId) {
        List<SkuImage> skuImages = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setSkuImageList(skuImages);
        return skuInfo;
    }*/
    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        //缓存的key值后缀：info
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
        //分布式锁的key后缀：lock
        String cacheKeyLock=RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;
        //先查询缓存
        SkuInfo skuInfo =(SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if(skuInfo==null){
            //加锁，，创建锁
            RLock lock = redissonClient.getLock(cacheKeyLock);
           /* *参数1：过期不候，过期的时间
            *   2：锁的持有时间
            *   3,：单位
            * */
            try {
                boolean isLock = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1,
                        RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
                if(isLock){//拿到锁
                    try {
                        //1.2DB中是否有数据
                        skuInfo = skuInfoMapper.selectById(skuId);
                        // 1.2.1：DB中有数据，将查询的数据返回
                        if(skuInfo!=null) {
                            List<SkuImage> skuImages = skuImageMapper.selectList(
                                    new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                            skuInfo.setSkuImageList(skuImages);
                            // 并将数据写入到缓存中
                            //RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300)缓存过期时间加上一个0-50分钟的随机值
                            // 二：解决了缓存雪崩问题
                            redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(3000), TimeUnit.SECONDS);
                        }else{
                            //一：解决了穿透问题
                            //1.2.DB中没有数据，将空结果加入到缓存中
                            redisTemplate.opsForValue().set(cacheKey,skuInfo,5,TimeUnit.MINUTES);
                        }
                    } finally {
                        //解锁
                       lock.unlock();
                    }
                }else {
                    //去查询缓存
                    try {//当一个人得到锁之后去查询数据库并放到缓存中，可能执行速度比较慢，先让其他线程先睡会
                        TimeUnit.SECONDS.sleep(300);
                        return  (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return  skuInfo;
    }
    //根据skuId获取sku信息,根据skuId 查询图片列表集合
/*
    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        //添加缓存
        */
/** RedisTemplate是进行封装之后的，
        * 原先的redisTemplate添加key值时会出现多几个符号乱码的问题
        * 使用RedisTemplate则不会出现这样的问题
        * 注：key值必须实现序列化接口
        * SkuInfo
        * 添加缓存
        *  1.1:缓存中有数据，直接将数据进行返回
        *  1.2:缓存中没有数据，去查询DB数据库
        *     1.2.1：将查询的数据返回
        *     1.2.2：并将数据写入到缓存中
        * *//*

        //缓存的key值    后缀：info
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
        //分布式锁的key   后缀：lock
        String cacheKeyLock=RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;
        SkuInfo skuInfo =(SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        //1.2:缓存中没有数据，去查询DB数据库
        if(skuInfo==null){
            //三：缓存的击穿问题
            String uuid = UUID.randomUUID().toString();
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(cacheKeyLock, uuid,RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
            if(isLock){
                try {
                    //拿到了锁
                    //1.2DB中是否有数据
                    skuInfo = skuInfoMapper.selectById(skuId);
                    if(skuInfo!=null) {
                        // 1.2.1：DB中有数据，将查询的数据返回
                        List<SkuImage> skuImages = skuImageMapper.selectList(
                                new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                        skuInfo.setSkuImageList(skuImages);
                        // 并将数据写入到缓存中
                         //RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300)缓存过期时间加上一个0-5分钟的随机值
                         //二：解决了缓存雪崩问题
                        redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300), TimeUnit.SECONDS);
                    }else{
                        //一：解决了穿透问题
                        //1.2.DB中没有数据，将空结果加入到缓存中
                        redisTemplate.opsForValue().set(cacheKey,skuInfo,5,TimeUnit.MINUTES);
                    }
                } finally {//三：缓存的击穿问题
                    //解锁，要保证原子性
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    //执行上面的LUA脚本会在redis上执行（redis是单线程（从而保证原子性））
                    redisTemplate.execute( new DefaultRedisScript<>(script), Collections.singletonList(cacheKeyLock), uuid);
                }
            }else {
                //去查询缓存
                try {//当一个人得到锁之后去查询数据库并放到缓存中，可能执行速度比较慢，先让其他线程先睡会
                    TimeUnit.SECONDS.sleep(5000);
                    return  (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // 1.1:缓存中有数据，直接将数据进行返回
        return skuInfo;
    }
*/
    //根据spuId，查询spu_sale_attr_value（被选中的）
    @Override
    @GmallCache(prefix = "getSpuSaleAttrListCheckBySku:")
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId,Long spuId) {
      return   spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId,spuId);
    }
    //根据spuId，skuId 查询销售属性集合
    @Override
    @GmallCache(prefix = "getSkuValueIdsMap:")
    public Map getSkuValueIdsMap(Long spuId) {
        Map map = new HashMap<>();
        List<Map> skuValueIdsMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        if (!CollectionUtils.isEmpty(skuValueIdsMap)){
            skuValueIdsMap.forEach(skuValueIds->{
                map.put(skuValueIds.get("value_ids"),skuValueIds.get("sku_id"));
            });
        }
        return map ;
    }
    //查询全部分类属性
    @Override
    public  List<BaseCategoryView> getBaseCategoryList() {
        return baseCategoryViewMapper.selectList(null);
    }

    //3.查询品牌
    @Override
    public BaseTrademark getBaseTrademark(Long tmId) {
        return baseTrademarkMapper.selectById(tmId);
    }
    // 5.平台属性集合
    @Override
    public List<SkuAttrValue> getSkuAttrValue(Long skuId) {
        return skuAttrValueMapper.getSkuAttrValueList(skuId);
    }
}
